﻿using JESAI.DistributedLock.Internal;
using System;
using System.Data;
using System.Threading;

namespace JESAI.DistributedLock.Postgres;

/// <summary>
/// 指定连接到Postgres数据库并锁定的选项
/// </summary>
public sealed class PostgresConnectionOptionsBuilder
{
    private TimeoutValue? _keepaliveCadence;
    private bool? _useTransaction, _useMultiplexing;

    internal PostgresConnectionOptionsBuilder() { }

    /// <summary>
    /// 一些Postgres设置具有适当的自动化，这会严重终止空闲连接。
    ///
    /// 为了防止这种情况，该选项设置了在持有锁的连接上运行无操作“keepalive”查询的节奏。
    /// 请注意，这仍然不能保证在调控器可能会杀死连接的所有情况下保护连接。
    ///
    /// 默认为<see cref="Timeout.InfiniteTimeSpan"/>，这将禁用keepalive。
    /// </summary>
    public PostgresConnectionOptionsBuilder KeepaliveCadence(TimeSpan keepaliveCadence)
    {
        this._keepaliveCadence = new TimeoutValue(keepaliveCadence, nameof(keepaliveCadence));
        return this;
    }

    /// <summary>
    ///同步是否应使用事务范围而不是会话范围。默认为false。
    ///
    ///使用某些pgbounger配置进行分布式锁定时，需要基于事务进行同步
    ///（请参见https://github.com/madelson/DistributedLock/issues/168#issuecomment-1823277173）。它也可能稍微少一些
    ///比为单个锁使用连接昂贵，因为释放只需要释放
    ///底层<see cref=“IDbTransaction”/>。
    ///
    ///该策略的缺点是它与<see cref="UseMultiplexing（bool）"/>不兼容，因此
    ///放弃了该方法的优势。
    /// </summary>
    public PostgresConnectionOptionsBuilder UseTransaction(bool useTransaction = true)
    {
        this._useTransaction = useTransaction;
        return this;
    }

    /// <summary>
    ///这种模式利用了这样一个事实，即当“持有”锁（或其他同步原语）时，连接本质上是空闲的。因此，与其为每个保持的锁创建新的连接，通常还可以多路复用共享连接，以便该连接可以同时保持多个锁。
    ///
    ///默认情况下，多路复用处于启用状态。
    ///
    ///这是以这样一种方式实现的，即释放在这种连接上保持的锁永远不会被
    ///正在等待获取同一连接上的锁的Acquire（）调用。由于这个原因，多路复用策略是“乐观的”：如果不能在共享连接上立即获得锁，则将分配一个新的（可共享）连接。
    ///
    ///此选项可以提高性能，并避免高负载场景中的连接池不足。它也是
    ///特别适用于<see cref="IDistributedLock.TryAcquire（TimeSpan，System.Threading.CancellationToken）"/>语义与零长度超时一起使用的情况。
    /// </summary>
    public PostgresConnectionOptionsBuilder UseMultiplexing(bool useMultiplexing = true)
    {
        this._useMultiplexing = useMultiplexing;
        return this;
    }

    internal static (TimeoutValue keepaliveCadence, bool useTransaction, bool useMultiplexing) GetOptions(
        Action<PostgresConnectionOptionsBuilder>? optionsBuilder)
    {
        PostgresConnectionOptionsBuilder? options;
        if (optionsBuilder != null)
        {
            options = new();
            optionsBuilder(options);
        }
        else
        {
            options = null;
        }

        var keepaliveCadence = options?._keepaliveCadence ?? Timeout.InfiniteTimeSpan;
        var useTransaction = options?._useTransaction ?? false;
        var useMultiplexing = options?._useMultiplexing ?? !options?._useTransaction ?? true;

        if (useMultiplexing && useTransaction)
        {
            throw new ArgumentException(nameof(UseTransaction) + ": is not compatible with " + nameof(UseMultiplexing));
        }

        return (keepaliveCadence, useTransaction, useMultiplexing);
    }
}